home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 276-300 / disk_281 / diffdir / diffdir.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  22KB  |  782 lines

  1. /*  DiffDir - Compare directories for differences.
  2.     Filename:   DiffDir.c
  3.     (C)Copyright 1988 by Mark R. Rinfret, All Rights Reserved.
  4.     This software may be freely distributed for non-profit use only.
  5.     You are free to make changes and redistribute this program as
  6.     long as the source is distributed and this notice is kept intact.
  7.  
  8.     History (most recent change first):
  9.  
  10.     07/04/89 V1.1 (MRR)
  11.         Added flags for specific tests.
  12.  
  13.     12/31/88 V1.0 (MRR)
  14.         Program conception and implementation.
  15.  
  16.     I wrote DiffDir to assist me with configuration management.  Though
  17.     I keep all of my PD files on floppy disk, I usually roll them onto
  18.     the hard disk when I want to make changes.  Sometimes, I forget to
  19.     copy the hard disk version back to floppy or I forget that I've already
  20.     done it.  DiffDir scans two directories and reports the following
  21.     discrepancies:
  22.  
  23.         1. File dates are different.
  24.         2. File protection flags are different.
  25.         3. File names are not exact (case discrepancy).
  26.         4. File sizes are different.
  27.         5. File comments are different.
  28.         6. File exists in one directory but not the other.
  29.  
  30.     DiffDir does not perform file content comparisons.  It will, however,
  31.     optionally generate a script for performing comparisons on files whose
  32.     attributes differ.
  33.  
  34.     Usage:  DiffDir [-C] [-S] [-D] [-c] [-s scriptfile] [-v] <dir1> <dir2>
  35.     Where:
  36.             -C suppresses the testing of comments
  37.  
  38.             -S suppresses the testing of file sizes
  39.  
  40.             -D suppresses the testing of file dates
  41.  
  42.             -c specifies that letter case should be ignored when comparing
  43.                filenames
  44.  
  45.             -s specifies that a file comparison script is to be output
  46.  
  47.             -v specifies verbose output
  48.  
  49.             <dir1> is the name of the first directory
  50.  
  51.             <dir2> is the name of the second directory
  52.  
  53. */
  54.  
  55. #include <stdio.h>
  56. #include <exec/types.h>
  57. #include <exec/memory.h>
  58. #include <libraries/dos.h>
  59. #include <functions.h>
  60.  
  61.  
  62. typedef struct fileList {
  63.     USHORT          fileCount;
  64.     char            *dName;     /* directory name for this list */
  65.     struct fileNode *firstEntry, *lastEntry;
  66.     } FileList;
  67.  
  68. typedef struct fileNode {
  69.     struct fileNode *next, *prev;
  70.     struct fileList *parentList;    /* the list that I belong to */
  71.     char            *name;
  72.     LONG            flags;          /* protection, other bits */
  73.     char            *comment;       /* NULL if comment was empty */
  74.     struct DateStamp date;
  75.     ULONG           size;           /* in bytes */
  76.     BOOL            isDir;          /* TRUE => node is a directory */
  77.     struct FileNode *subList;       /* sublist for directory node */
  78.     } FileNode;
  79.  
  80. #define NAMES_DONT_MATCH    1
  81. #define DATES_DONT_MATCH    2
  82. #define FLAGS_DONT_MATCH    4
  83. #define SIZES_DONT_MATCH    8
  84. #define COMMENTS_DONT_MATCH 16
  85.  
  86. #define ITEM_COUNT          5   /* Make sure this tracks the list above! */
  87.  
  88. static char *errorDesc[ITEM_COUNT] = {
  89.         " names ", " dates ", " flags ", " sizes ", " comments " };
  90.  
  91. char                    *DupString();
  92. FileNode                *FindFile();
  93. void                    FreeNode();
  94. char                    *MakeDirName();
  95. void                    *MyAlloc();
  96. void                    MyExit();
  97. void                    ReportStats();
  98. void                    WriteFileInfo();
  99.  
  100.  
  101. struct FileInfoBlock    *fib;
  102. BOOL                    ignoreCase = FALSE;
  103. USHORT                  level = 0;
  104. FileList                list1, list2;
  105. LONG                    maxMemUsed, memInUse;
  106. BOOL                    outputScript = FALSE;
  107. FILE                    *scriptFile;
  108. ULONG                   testFlags = 0;
  109. LONG                    totalFiles, totalDirs;
  110. BOOL                    verbose = FALSE;
  111.  
  112. main(argc, argv)
  113.     int argc; char **argv;
  114. {
  115.     char    flag;
  116.  
  117.     testFlags = 0xFFFFFFFF;
  118.  
  119.     while (--argc > 0 && **++argv == '-') {
  120.         flag = (*argv)[1];
  121.         switch (flag) {
  122.             case 'C':               /* Ignore comments. */
  123.                 testFlags &= (~COMMENTS_DONT_MATCH);
  124.                 break;
  125.             case 'D':               /* Suppress date test. */
  126.                 testFlags &= (~DATES_DONT_MATCH);
  127.                 break;
  128.             case 'F':               /* Ignore flag differences */
  129.                 testFlags &= (~FLAGS_DONT_MATCH);
  130.                 break;
  131.             case 'S':               /* Ignore size differences (?!?) */
  132.                 testFlags &= (~SIZES_DONT_MATCH);
  133.                 break;
  134.             case 'c':
  135.                 ignoreCase = TRUE;
  136.                 break;
  137.             case 's':
  138.                 if (--argc) {
  139.                     ++argv;
  140.                     scriptFile = fopen(*argv, "w");
  141.                     if (!scriptFile) {
  142.                         perror("Script file would not open!");
  143.                         exit(1);
  144.                     }
  145.                 }
  146.                 else
  147.                     Usage();
  148.                 break;
  149.             case 'v':
  150.                 verbose = TRUE;
  151.                 break;
  152.             default:
  153.                 Usage();
  154.         }
  155.     }
  156.     if (argc != 2) Usage();
  157.     list1.dName = MakeDirName("",*argv++);
  158.     list2.dName = MakeDirName("",*argv);
  159.     /* fib must be longword aligned, thus the AllocMem call. */
  160.     fib = AllocMem((long) sizeof(*fib), MEMF_PUBLIC|MEMF_CLEAR);
  161.     if (fib == NULL) {
  162.         printf("DiffDir: unable to allocate file info block!\n");
  163.         goto done;
  164.     }
  165.  
  166.     if (! CollectFiles(&list1))
  167.         if (! CollectFiles(&list2))
  168.             CompareLists(&list1, &list2);
  169. done:
  170.     if (fib) FreeMem(fib, (long) sizeof(*fib));
  171.     if (verbose) ReportStats();
  172. }
  173.  
  174. /*  FUNCTION
  175.         AddNode - add file info node to list.
  176.  
  177.     SYNOPSIS
  178.         AddNode(node, list)
  179.             FileNode *node;
  180.             FileList *list;
  181.  
  182.     DESCRIPTION
  183.         AddNode adds the <node> to the <list>.  Right now, a very lazy
  184.         approach is taken (adds to end of list).  Perhaps later, we'll
  185.         make the list a binary tree or better.
  186.  
  187. */
  188.  
  189. void
  190. AddNode(node, list)
  191.     FileNode *node; FileList *list;
  192. {
  193.     if (list->firstEntry) {         /* List has stuff in it? */
  194.         list->lastEntry->next = node;
  195.     }
  196.     else {
  197.         list->firstEntry = node;    /* This is the first entry. */
  198.     }
  199.     node->prev = list->lastEntry;
  200.     list->lastEntry = node;
  201.     ++list->fileCount;
  202.     if (node->isDir)
  203.         ++totalDirs;
  204.     else
  205.         ++totalFiles;
  206. }
  207.  
  208. /*  FUNCTION
  209.         CollectFiles - collect files for one directory level.
  210.  
  211.     SYNOPSIS
  212.         int CollectFiles(list)
  213.             FileList *list;
  214.  
  215.     DESCRIPTION
  216.         CollectFiles scans the directory pointed to by <list> and creates
  217.         list entry nodes for each file or directory found.  A zero is
  218.         returned on success, non-zero otherwise.
  219. */
  220.  
  221. int
  222. CollectFiles(list)
  223.     FileList *list;
  224. {
  225.     int         errCode;
  226.     struct Lock *lock = NULL;
  227.     FileNode    *fNode;
  228.     int         result = 0;
  229.  
  230.     if (verbose)
  231.         printf("DiffDir: scanning '%s'\n", list->dName);
  232.  
  233.     lock = (struct Lock *) Lock(list->dName, SHARED_LOCK);
  234.     if (lock == NULL) {
  235.         result = IoErr();
  236.         printf("DiffDir: failed to lock '%s'!\n", list->dName);
  237.         goto done;
  238.     }
  239.     if (Examine(lock, fib) == 0) {
  240.         result = IoErr();
  241.         printf("DiffDir: failed to get info for '%s'!\n", list->dName);
  242.         goto done;
  243.     }
  244.  
  245.     if (fib->fib_DirEntryType < 0) {
  246.         result = -1;
  247.         printf("DiffDir: '%s' is not a directory!\n", list->dName);
  248.         goto done;
  249.     }
  250.  
  251.     while (!result && ExNext(lock, fib)) {
  252.         fNode = MyAlloc(sizeof(FileNode));
  253.         fNode->parentList = list;
  254.         fNode->name = DupString(fib->fib_FileName);
  255.         fNode->isDir = (fib->fib_DirEntryType > 0);
  256.         fNode->flags = fib->fib_Protection;
  257.         if (*fib->fib_Comment)
  258.             fNode->comment = DupString(fib->fib_Comment);
  259.         fNode->date = fib->fib_Date;
  260.         fNode->size = fib->fib_Size;
  261.         AddNode(fNode, list);
  262.     }
  263.     errCode = IoErr();
  264.     if (errCode != ERROR_NO_MORE_ENTRIES) {
  265.         result = errCode;
  266.         printf("DiffDir: scan of directory '%s' failed!\n", list->dName);
  267.     }
  268. done:
  269.     if (lock) UnLock(lock);
  270.     return result;
  271. }
  272.  
  273. /*  FUNCTION
  274.         CompareLists - compare files and directories in two lists.
  275.  
  276.     SYNOPSIS
  277.         int CompareLists(l1, l2)
  278.             FileList *l1, *l2;
  279.  
  280.     DESCRIPTION
  281.         Comparelists first makes an overall assessment of lists <l1> and
  282.         <l2>.  If the number of files/directories in the lists differ,
  283.         that fact is reported.  Next, CompareLists tests for the condition
  284.         where an entry in one list has the same name as an entry in the
  285.         other list, but one entry represents a file and the other entry
  286.         represents a directory.  These entries are removed from the list.
  287.         CompareFiles is then called to compare all file nodes, removing
  288.         them as they are "used".  Finally, CompareDirs is called to
  289.         compare all directory nodes, again removing the nodes as they
  290.         are used. A non-zero return indicates a fatal error.
  291. */
  292. int
  293. CompareLists(l1, l2)
  294.     FileList *l1, *l2;
  295. {
  296. static char *isDirMsg =     " is a directory";
  297. static char *isFileMsg =    " is a file";
  298.  
  299.     FileNode    *f1, *f2, *nextEntry;
  300.     int         i;
  301.     int result = 0;
  302.  
  303.     ++level;
  304.     if (verbose) {
  305.         printf("DiffDir: comparing directory\n '%s' to '%s'\n",
  306.                l1->dName, l2->dName);
  307.     }
  308.     /* Scan the lists for nodes whose names match but whose types
  309.        differ (file vs. directory).
  310.     */
  311.     for (f1 = l1->firstEntry; f1; f1 = nextEntry) {
  312.         nextEntry = f1->next;
  313.         f2 = FindFile(f1, l2);
  314.         if (f2 && (f2->isDir != f1->isDir) ) {  /* Ooops! */
  315.             printf("*** '%s%s' %s\n*** but '%s%s' %s!\n",
  316.                    l1->dName,f1->name, f1->isDir ? isDirMsg : isFileMsg,
  317.                    l2->dName,f2->name, f2->isDir ? isDirMsg : isFileMsg);
  318.             FreeNode(f1, l1);
  319.             FreeNode(f2, l2);
  320.         }
  321.     }
  322.     if (! (result = CompareFiles(l1, l2)))
  323.         result = CompareDirs(l1, l2);
  324.     --level;
  325.     return result;
  326. }
  327.  
  328. /*  FUNCTION
  329.         CompareDirs - compare directory entries.
  330.  
  331.     SYNOPSIS
  332.         int CompareDirs(list1, list2)
  333.             FileList *list1, *list2;
  334.  
  335.     DESCRIPTION
  336.         CompareDirs scans <list1>, attempting to match its directory node
  337.         entries with entries in <list2>.  For each matching entry found,
  338.         CompareDirs creates a new sublist, recursively calling CompareLists
  339.         to compare the contents of those directories.  When CompareLists
  340.         returns, CompareDirs removes the "used" directory entries from
  341.         both lists. A non-zero return code indicates a fatal error.
  342. */
  343.  
  344. int
  345. CompareDirs(list1, list2)
  346.     FileList *list1, *list2;
  347. {
  348. static char *missing = "*** Directory missing: '%s%s'\n";
  349.  
  350.     FileNode *n1, *n2, *nextEntry;
  351.     int      result = 0;
  352.     FileList *subList1, *subList2;
  353.  
  354.     for (n1 = list1->firstEntry; n1 && !result; n1 = nextEntry) {
  355.         nextEntry = n1->next;
  356.         /* Note: there should only be directory nodes in the list
  357.            at this point!
  358.         */
  359.         if (! n1->isDir) {
  360.             puts("DiffDir: non-directory node found in CompareDirs!");
  361.             MyExit();                   /* Dis be real bad! */
  362.         }
  363.         n2 = FindFile(n1, list2);
  364.         if (n2 == NULL) {
  365.             printf(missing, list2->dName, n1->name);
  366.         }
  367.         else {
  368.             subList1 = MyAlloc( sizeof(FileList) );
  369.             subList1->dName = MakeDirName(list1->dName, n1->name);
  370.             subList2 = MyAlloc( sizeof(FileList) );
  371.             subList2->dName = MakeDirName(list2->dName, n2->name);
  372.             result = CollectFiles(subList1);
  373.             if (!result)
  374.                 result = CollectFiles(subList2);
  375.             if (!result)
  376.                 result = CompareLists(subList1, subList2);
  377.  
  378.             /* Give back the memories :-) */
  379.             free(subList1->dName);
  380.             free(subList1);
  381.             free(subList2->dName);
  382.             free(subList2);
  383.         }
  384.         FreeNode(n1, list1);
  385.         if (n2) FreeNode(n2, list2);
  386.     }
  387.     if (!result) {
  388.         for (n2 = list2->firstEntry; n2; n2 = nextEntry) {
  389.             nextEntry = n2->next;
  390.             printf(missing, list1->dName, n2->name);
  391.             FreeNode(n2, list2);
  392.         }
  393.     }
  394.     return result;
  395. }
  396.  
  397. /*  FUNCTION
  398.         CompareFile - compare the attributes of two similar files.
  399.  
  400.     SYNOPSIS
  401.         void CompareFile(f1, f2)
  402.              FileNode *f1, *f2;
  403.  
  404.     DESCRIPTION
  405.         CompareFile is called with two file description nodes, <f1> and
  406.         <f2> which are expected to represent similar files in different
  407.         directory hierarchies.  CompareFile compares the currently selected
  408.         file attributes and will report any discrepancies to standard output.
  409. */
  410. void
  411. CompareFile(f1, f2)
  412.     FileNode *f1, *f2;
  413. {
  414.     USHORT error = 0, item, mask;
  415.  
  416.     if (f1->isDir != f2->isDir) {
  417.         puts("*** File type mismatch (file vs. dir) - program error!");
  418.         FreeNode(f1, f1->parentList);
  419.         FreeNode(f2, f2->parentList);
  420.     }
  421.     else {
  422.         if ((testFlags & FLAGS_DONT_MATCH) && (f1->flags != f2->flags) )
  423.             error |= FLAGS_DONT_MATCH;
  424.  
  425.         if ((testFlags & DATES_DONT_MATCH) && 
  426.             (CompareDS(&f1->date, &f2->date) ) )
  427.             error |= DATES_DONT_MATCH;
  428.  
  429.         if (!ignoreCase) {
  430.             if (strcmp(f1->name, f2->name) != 0)
  431.                 error |= NAMES_DONT_MATCH;
  432.         }
  433.  
  434.         if ( (testFlags & SIZES_DONT_MATCH) && (f1->size != f2->size) ) {
  435.             error |= SIZES_DONT_MATCH;
  436.             if (scriptFile)
  437.                 fprintf(scriptFile,"%%COMPARE%% %s%s %s%s\n",
  438.                         f1->parentList->dName,f1->name,
  439.                         f2->parentList->dName,f2->name);
  440.         }
  441.         if ((testFlags & COMMENTS_DONT_MATCH) &&
  442.             (strcmp(f1->comment, f2->comment) != 0) )
  443.             error |= COMMENTS_DONT_MATCH;
  444.     }
  445.     if (error) {                    /* Aw, darn... */
  446.         printf("*** Mismatch: ");
  447.         for (item = 0, mask = 1; item < ITEM_COUNT;
  448.              ++item, mask= (mask << 1)) {
  449.             if (error & mask)
  450.                 printf(errorDesc[item]);
  451.         }
  452.         puts("");
  453.         puts(f1->parentList->dName);
  454.         WriteFileInfo(f1);
  455.         puts("------------------------------------");
  456.         puts(f2->parentList->dName);
  457.         WriteFileInfo(f2);
  458.         puts("====================================");
  459.     }
  460. }
  461.  
  462. /*  FUNCTION
  463.         CompareFiles - compare all file nodes in two lists.
  464.  
  465.     SYNOPSIS
  466.         int CompareFiles(l1, l2)
  467.             FileList *l1, *l2;
  468.  
  469.     DESCRIPTION
  470.         The file attributes for all files in list <l1> are compared to
  471.         those in list <l2>.  Discrepancies are reported to standard
  472.         output.  After all the files in <l1> have been tested, a second
  473.         scan is made over list <l2> for any remaining file nodes.  These
  474.         represent files which were not found in <l1>.  Upon return, all
  475.         file nodes will have been removed from lists <l1> and <l2>,
  476.         leaving behind any directory nodes for CompareDirs().
  477. */
  478.  
  479. int
  480. CompareFiles(l1, l2)
  481.     FileList *l1, *l2;
  482. {
  483.     static char *missing = "*** File missing: '%s%s'\n";
  484.     FileNode    *f1, *f2;
  485.  
  486.     /* Loop through all file entries in list1. */
  487.     for (f1 = l1->firstEntry; f1; f1 = f1->next) {
  488.         if (f1->isDir) continue;
  489.         f2 = FindFile(f1, l2);
  490.         if (f2 == NULL) {
  491.             printf(missing, l2->dName, f1->name);
  492.         }
  493.         else {
  494.             CompareFile(f1, f2);
  495.         }
  496.         FreeNode(f1, l1);
  497.         if (f2)
  498.             FreeNode(f2, l2);
  499.     }
  500.  
  501.     /* Look for "leftovers" in list 2. */
  502.     for (f2 = l2->firstEntry; f2; f2 = f2->next) {
  503.         if (f2->isDir) continue;
  504.         printf(missing, l1->dName, f2->name);
  505.         FreeNode(f2, l2);
  506.     }
  507.     return 0;
  508. }
  509.  
  510. /*  FUNCTION
  511.         DupString - duplicate a string.
  512.  
  513.     SYNOPSIS
  514.         char *DupString(oldString)
  515.               char *oldString;
  516.  
  517.     DESCRIPTION
  518.         DupString dynamically allocates space for a new copy of <oldString>,
  519.         copies <oldString> to the new area and returns a pointer to the new
  520.         string.
  521.  
  522. */
  523.  
  524. char *
  525. DupString(oldString)
  526.     char *oldString;
  527. {
  528.     char *newString;
  529.  
  530.     newString = MyAlloc(strlen(oldString)+1);
  531.     strcpy(newString, oldString);
  532.     return newString;
  533. }
  534.  
  535. /*  FUNCTION
  536.         FindFile - find a file node by name.
  537.  
  538.     SYNOPSIS
  539.         FileNode *FindFile(node, list)
  540.                   FileNode *node;
  541.                   FileList *list;
  542.  
  543.     DESCRIPTION
  544.         FindFile searches <list> for a file description node whose name
  545.         matches the name in <node>.  A case-insensitive name comparison
  546.         is performed.  If the matching entry is found, a pointer to it
  547.         is returned.  Otherwise, NULL is returned.
  548. */
  549.  
  550. FileNode *
  551. FindFile(node, list)
  552.     FileNode *node; FileList *list;
  553. {
  554.     FileNode *tNode;
  555.  
  556.     for (tNode = list->firstEntry; tNode; tNode = tNode->next) {
  557.         if (stricmp(node->name, tNode->name) == 0)
  558.             return tNode;
  559.     }
  560.     return NULL;                    /* Sorry...not found. */
  561. }
  562.  
  563. /*  FUNCTION
  564.         FreeNode - free a file node from a list.
  565.  
  566.     SYNOPSIS
  567.         void FreeNode(node, list)
  568.              FileNode *node;
  569.              FileList *list;
  570. */
  571. void
  572. FreeNode(node, list)
  573.         FileNode *node; FileList *list;
  574. {
  575.     if (node->prev)
  576.         node->prev->next = node->next;
  577.     if (node->next)
  578.         node->next->prev = node->prev;
  579.     if (node == list->firstEntry)
  580.         list->firstEntry = node->next;
  581.     if (node == list->lastEntry)
  582.         list->lastEntry = node->prev;
  583.  
  584.     free(node->name);
  585.     free(node->comment);
  586.     free(node);
  587. }
  588.  
  589. /*  FUNCTION
  590.         MakeDirName - assemble a directory name from components.
  591.  
  592.     SYNOPSIS
  593.         char *MakeDirName(s1, s2)
  594.               char *s1, *s2;
  595.  
  596.     DESCRIPTION
  597.         MakeDirName dynamically allocates a string large enough to hold
  598.         a composite name formed from strings <s1> and <s2>. It also adds
  599.         a directory separator (/) to the end of the new name if the
  600.         new result does not end in a colon (:).  The new name is returned
  601.         as the function result.
  602. */
  603. char *
  604. MakeDirName(s1, s2)
  605.     char *s1, *s2;
  606. {
  607.     char    *index();
  608.  
  609.     char    *dirName;
  610.  
  611.     dirName = MyAlloc(strlen(s1)+strlen(s2)+2);
  612.     strcpy(dirName, s1);
  613.     strcat(dirName, s2);
  614.     if (dirName[strlen(dirName)-1] != ':') strcat(dirName, "/");
  615.     return dirName;
  616. }
  617.  
  618. /*  FUNCTION
  619.         MyAlloc - perform memory allocation with error checking.
  620.  
  621.     SYNOPSIS
  622.         void *MyAlloc(size)
  623.               USHORT size;
  624.  
  625.     DESCRIPTION
  626.         MyAlloc attempts to allocate <size> bytes of memory.  If it fails,
  627.         an error message is sent to standard output and the program is
  628.         terminated.  Otherwise, MyAlloc returns a pointer to the newly
  629.         allocated (zero-filled) memory block.
  630. */
  631. void *
  632. MyAlloc(size)
  633.     USHORT size;
  634. {
  635.     void *calloc();
  636.  
  637.     void *ptr;
  638.  
  639.     ptr = calloc(size, 1);
  640.     if (ptr == NULL) {
  641.         printf("DiffDir: failed to allocate %ld bytes!\n", size);
  642.         MyExit();
  643.     }
  644.     memInUse += size;
  645.     if (memInUse > maxMemUsed) maxMemUsed = memInUse;
  646.     return ptr;
  647. }
  648.  
  649. /*  FUNCTION
  650.         MyExit - terminate program with cleanup.
  651.  
  652.     SYNOPSIS
  653.         void MyExit();
  654.  
  655.     DESCRIPTION
  656.         MyExit simply provides a graceful way for the program to exit,
  657.         performing any necessary cleanup chores.
  658. */
  659. void
  660. MyExit()
  661. {
  662.     if (fib) FreeMem(fib, (long) sizeof(*fib));
  663.     puts("DiffDir: abnormal exit!");
  664.     ReportStats();
  665.     exit(1);
  666. }
  667. /*  FUNCTION
  668.         ReportStats - report program statistics.
  669.  
  670.     SYNOPSIS
  671.         void ReportStats();
  672.  
  673.     DESCRIPTION
  674.         ReportMem reports the maximum memory used, total number of file
  675.         nodes and total number of directory nodes for this invocation
  676.         of DiffDir, ONLY if the verbose option is turned on or if the
  677.         program terminates abnormally.
  678. */
  679. void
  680. ReportStats()
  681. {
  682.     printf("DiffDir: Files: %ld; directories: %ld; max memory: %ld bytes\n",
  683.            totalFiles, totalDirs, maxMemUsed);
  684. }
  685.  
  686.  
  687. /*  FUNCTION
  688.         stricmp - perform a case-insensitive string compare.
  689.  
  690.     SYNOPSIS
  691.         int stricmp(s1, s2)
  692.             char *s1, *s2;
  693.  
  694.     DESCRIPTION
  695.         Strings <s1> and <s2> are compared, ignoring differences in case.
  696.         A result code is returned according to the following:
  697.             0   => strings match
  698.            <0   => s1 < s2
  699.            >0   => s1 > s2
  700. */
  701.  
  702. int
  703. stricmp(s1, s2)
  704.     register char *s1, *s2;
  705. {
  706.     int c1, c2, cd;
  707.  
  708.     do {
  709.         c1 = tolower(*s1++);
  710.         c2 = tolower(*s2++);
  711.         if (cd = (c1 - c2)) break;
  712.     } while (c1 && c2);
  713.  
  714.     return cd;
  715. }
  716.  
  717. /*  FUNCTION
  718.         Usage - describe program usage and exit.
  719.  
  720.     SYNOPSIS
  721.         void Usage();
  722.  
  723.     DESCRIPTION
  724.         Usage is called when the user invokes DiffDir with incorrect
  725.         or insufficient parameters.  The correct invocation syntax
  726.         is displayed and the program is terminated.
  727. */
  728. Usage()
  729. {
  730.     puts("Usage: DiffDir [-C] [-D] [-S] [-c] [-s scriptfile] dirname1 dirname2");
  731.     MyExit();
  732. }
  733.  
  734. /*  FUNCTION
  735.         WriteFileInfo - write a full file description to standard output.
  736.  
  737.     SYNOPSIS
  738.         void WriteFileInfo(node)
  739.              FileNode *node;
  740.  
  741.     DESCRIPTION
  742.         WriteFileInfo writes complete info about the file specified by
  743.         <node> to the standard output.  This only happens when an error
  744.         occurs.
  745.  
  746. */
  747.  
  748. void
  749. WriteFileInfo(node)
  750.     FileNode *node;
  751.  
  752. {
  753.     static char flagSetNames[9] = {
  754.         '-', '-', '-', '-', 'a', 'p', 's', '?', '?'
  755.         };
  756.     static char flagClearNames[9] = {
  757.         'd', 'e', 'w', 'r', '-', '-', '-', '-', '-'
  758.         };
  759.  
  760.     ULONG   flags;
  761.     SHORT   i;
  762.     ULONG   mask;
  763.     char    temp[30];
  764.  
  765.     DSToStr(temp,"%02m-%02d-%02y %02h:%02n:%02s ",&node->date);
  766.     printf(temp);
  767.     flags = node->flags;
  768.     for (i = 0, mask = 1; i < 9; ++i, mask = (mask << 1) )
  769.         if (flags & mask)
  770.             temp[8 - i] = flagSetNames[i];
  771.         else
  772.             temp[8 - i] = flagClearNames[i];
  773.  
  774.     temp[9] = '\0';
  775.  
  776.     printf("%s %8ld %s\n", temp, node->size, node->name);
  777.     if (node->comment)
  778.         printf(": %s\n",node->comment);
  779. }
  780.  
  781.  
  782.